package org.sunrise.game.http.server;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletHolder;
import org.eclipse.jetty.server.Server;
import org.sunrise.game.center.client.ReportClient;
import org.sunrise.game.center.client.ReportClientManager;
import org.sunrise.game.center.server.NodeManager;
import org.sunrise.game.common.config.ConfigReader;
import org.sunrise.game.common.log.LogCore;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Data
public class HttpServer {
    private final int port;
    private final Server server;
    public static final Map<String, String> responses = new HashMap<>();
    private final Random random = new Random();
    // uid-gameId
    private final Map<String, Integer> uidGameIds = new HashMap<>();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private ReportClient reportClient;

    public HttpServer(int port) {
        this.port = port;
        server = new Server(port);
        ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
        handler.setContextPath("/");
        server.setHandler(handler);

        handler.addServlet(new ServletHolder(new HttpServlet() {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
                String response = null;
                if (req.getRequestURI().equals("/game_id")) {
                    String uid = req.getParameter("uid");
                    if (uid == null) {
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, "uid error");
                        return;
                    }
                    JSONArray gameList = JSONArray.parseArray(responses.get("/game_id_list"));
                    Set<Integer> list = new HashSet<>();
                    for (int i = 0; i < gameList.size(); i++) {
                        JSONObject jsonObject = gameList.getJSONObject(i);
                        int id = jsonObject.getIntValue("server_id");
                        list.add(id);
                    }
                    JSONObject jsonResponse = new JSONObject();
                    if (!list.isEmpty()) {
                        Integer lastGameId = uidGameIds.get(uid);
                        if (lastGameId != null && list.contains(lastGameId)) {
                            jsonResponse.put("server_id", lastGameId);
                        } else {
                            int gameId = list.stream().toList().get(random.nextInt(list.size()));
                            jsonResponse.put("server_id", gameId);
                            uidGameIds.put(uid, gameId);
                        }
                    } else {
                        jsonResponse.put("server_id", 0);
                        uidGameIds.remove(uid);
                    }
                    response = jsonResponse.toJSONString();
                } else if (req.getRequestURI().equals("/external_address")) {
                    String type = req.getParameter("type");
                    if (type == null) {
                        resp.sendError(HttpServletResponse.SC_NOT_FOUND, "type error");
                        return;
                    }
                    JSONArray externalList = JSONArray.parseArray(responses.get("/external_address_list"));
                    List<String> list = new ArrayList<>();
                    for (int i = 0; i < externalList.size(); i++) {
                        JSONObject jsonObject = externalList.getJSONObject(i);
                        if (jsonObject.getString("type").equals(type)) {
                            String address = jsonObject.getString("address");
                            list.add(address);
                        }
                    }
                    JSONObject jsonResponse = new JSONObject();
                    if (!list.isEmpty()) {
                        jsonResponse.put("address", list.get(random.nextInt(list.size())));
                    } else {
                        jsonResponse.put("address", "");
                    }
                    response = jsonResponse.toJSONString();
                } else if (req.getRequestURI().equals("/game_id_list")) {
                    response = responses.get(req.getRequestURI());
                } else if (req.getRequestURI().equals("/external_address_list")) {
                    response = responses.get(req.getRequestURI());
                }
                if (response != null) {
                    resp.setContentType("application/json");
                    resp.getWriter().write(response);
                } else {
                    resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Not Found");
                }
            }
        }), "/*");
    }

    public void start() {
        scheduler.scheduleAtFixedRate(HttpReportMessageManager::check, 0, 2, TimeUnit.SECONDS);

        try {
            server.start();

            LogCore.HttpServer.info("HttpServer start success, port = {}", port);
            server.join();
        } catch (Exception e) {
            LogCore.HttpServer.error("HttpServer start failed, reason : ", e);
        }
    }

    public void connectMaster() {
        Properties properties = ConfigReader.getProp();
        if (properties == null) {
            return;
        }
        int id = Integer.parseInt(properties.getProperty("master.id"));
        String masterIp = properties.getProperty("master.address");
        int masterPort = Integer.parseInt(properties.getProperty("master.port"));

        this.reportClient = ReportClientManager.createReportClient("HttpServer" + "-ReportClient", NodeManager.NODE_TYPE_HTTP);
        reportClient.getConnectToCenter().setMessageManager(new HttpReportMessageManager(reportClient.getConnectToCenter().getNodeId()));
        reportClient.connectMaster(masterIp, masterPort);
    }
}
